/*-------------------------------------------------------------------------
 *
 * basebackup_sink.h
 *	  用于过滤或发送基础备份过程中生成的档案
 *	  到最终目标的 API。
 *
 * 进行基础备份会为每个表空间目录生成一个档案，
 * 除非该功能已被禁用，否则还会生成备份清单。备份
 * 过程的目标是将这些档案和清单放置到某个地方，
 * 可能在以某种方式进行后处理之后。‘bbsink’
 * 是一个可以接收这些档案及（如果存在）清单的对象。
 *
 * 实际上，会有一系列的‘bbsink’对象，而不仅仅是一个，
 * 回调将从一个转发到另一个，可能会经过修改。每个
 * 对象负责执行单个任务，例如命令进度报告、限制流量或
 * 与客户端的通信。
 *
 * Portions Copyright (c) 2010-2022, PostgreSQL Global Development Group
 *
 * src/include/backup/basebackup_sink.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef BASEBACKUP_SINK_H
#define BASEBACKUP_SINK_H

#include "access/xlog_internal.h"
#include "common/compression.h"
#include "nodes/pg_list.h"

/* 前向声明。 */
struct bbsink;
struct bbsink_ops;
typedef struct bbsink bbsink;
typedef struct bbsink_ops bbsink_ops;

/*
 * 所有bbsink对象共享的备份的整体备份状态。
 *
 * 在调用bbstate_begin_backup之前，调用者必须初始化一个
 * bbsink_state对象，该对象将持续整个备份的生命周期，并且
 * 之后必须在每次调用bbsink方法之前根据需要更新它。
 * bbsink将保留对状态对象的指针，并将参照它以了解
 * 备份的进度。
 *
 * 'tablespaces'是一个tablespaceinfo对象的列表。在调用
 * bbstate_begin_backup()之前必须设置，并且之后不得修改。
 *
 * 'tablespace_num'是当前表空间在'tablespaces'列表中的索引。
 *
 * 'bytes_done'是迄今为止从$PGDATA读取的字节数。
 *
 * 'bytes_total'是估计在$PGDATA中存在的总字节数，如果我们已有
 * 此估计。
 *
 * 'bytes_total_is_valid'当且仅当一个正确的估计被存储到
 * 'bytes_total'中时为真。
 *
 * 'startptr'和'starttli'标识备份开始时WAL流中的点。
 * 必须在调用bbstate_begin_backup()之前设置，并且
 * 之后不得修改。
 */
typedef struct bbsink_state
{
	List	   *tablespaces;
	int			tablespace_num;
	uint64		bytes_done;
	uint64		bytes_total;
	bool		bytes_total_is_valid;
	XLogRecPtr	startptr;
	TimeLineID	starttli;
} bbsink_state;

/*
 * 任何类型的基础备份接收器的公共数据。
 *
 * 'bbs_ops'是相关的回调表。
 *
 * 'bbs_buffer'是要存储发送到bbsink的数据的缓冲区。
 * 它必须是BLCKSZ的倍数。
 *
 * 'bbs_buffer_length'是缓冲区的分配长度。
 *
 * 'bbs_next'是指向另一个bbsink的指针，该bbsink将
 * 一些或所有操作转发给它。
 *
 * 'bbs_state'是指向此备份的bbsink_state对象的指针。
 * 与此备份相关的每个bbsink都应指向相同的基础
 * 状态对象。
 *
 * 通常期望在创建bbsink时设置这些字段的值，并且
 * 之后不再更改。可以修改指向的数据显示的内容
 * bbs_buffer或bbs_state，但不应对该结构的内容
 * 进行更改。
 */
struct bbsink
{
	const bbsink_ops *bbs_ops;
	char	   *bbs_buffer;
	size_t		bbs_buffer_length;
	bbsink	   *bbs_next;
	bbsink_state *bbs_state;
};

/*
 * 基础备份接收器的回调。
 *
 * 所有这些回调都是必需的。如果特定回调只需将
 * 调用转发到sink->bbs_next，使用bbsink_forward_<callback_name>
 * 作为回调。
 *
 * 调用者应始终通过bbsink_*内联函数调用这些回调，而不是直接调用它们。
 */
struct bbsink_ops
{
	/*
	 * 此回调在备份开始时仅调用一次。它必须将
	 * bbs_buffer设置为指向一个存储块，在该存储块中
	 * 至少可以写入bbs_buffer_length字节的数据。
	 */
	void		(*begin_backup) (bbsink *sink);

	/*
	 * 对于传输到bbsink的每个存档，将调用一次
	 * begin_archive()回调，对于archive_contents()回调将调用若干次，
	 * 然后调用一次end_archive()回调。
	 *
	 * 在调用archive_contents()回调之前，调用者应将
	 * 与len相等的字节数复制到bbs_buffer中，但不超过
	 * bbs_buffer_length。
	 *
	 * 通常，如果在调用archive_contents()回调之前缓冲区尽可能满
	 * 是好的，但为确保其绝对100%满而消耗额外周期并不值得。
	 */
	void		(*begin_archive) (bbsink *sink, const char *archive_name);
	void		(*archive_contents) (bbsink *sink, size_t len);
	void		(*end_archive) (bbsink *sink);

	/*
	 * 如果要将备份清单传输给bbsink，将调用一次
	 * begin_manifest()回调，若干次调用manifest_contents()回调，
	 * 然后调用一次end_manifest()回调。这些调用将在所有档案
	 * 传输后发生。
	 *
	 * 调用manifest_contents()回调的规则与上面的
	 * archive_contents()回调相同。
	 */
	void		(*begin_manifest) (bbsink *sink);
	void		(*manifest_contents) (bbsink *sink, size_t len);
	void		(*end_manifest) (bbsink *sink);

	/*
	 * 此回调在所有归档和清单发送后仅调用一次。
	 */
	void		(*end_backup) (bbsink *sink, XLogRecPtr endptr, TimeLineID endtli);

	/*
	 * 如果备份因错误而被中止，则在销毁bbsink对象之前调用该回调，
	 * 以便它可以释放任何不会被自动释放的资源。
	 * 如果没有发生错误，则在end_backup回调后调用该回调。
	 */
	void		(*cleanup) (bbsink *sink);
};

/* 开始备份。 */
static inline void
bbsink_begin_backup(bbsink *sink, bbsink_state *state, int buffer_length)
{
	Assert(sink != NULL);

	Assert(buffer_length > 0);

	sink->bbs_state = state;
	sink->bbs_buffer_length = buffer_length;
	sink->bbs_ops->begin_backup(sink);

	Assert(sink->bbs_buffer != NULL);
	Assert((sink->bbs_buffer_length % BLCKSZ) == 0);
}

/* 开始档案。 */
static inline void
bbsink_begin_archive(bbsink *sink, const char *archive_name)
{
	Assert(sink != NULL);

	sink->bbs_ops->begin_archive(sink, archive_name);
}

/* 处理归档的一部分内容。 */
static inline void
bbsink_archive_contents(bbsink *sink, size_t len)
{
	Assert(sink != NULL);

	/*
	 * 调用者应合理尝试在调用此函数之前填充缓冲区，
	 * 因此它不应完全为空。也不应超出容量填充。
	 */
	Assert(len > 0 && len <= sink->bbs_buffer_length);

	sink->bbs_ops->archive_contents(sink, len);
}

/* 完成归档。 */
static inline void
bbsink_end_archive(bbsink *sink)
{
	Assert(sink != NULL);

	sink->bbs_ops->end_archive(sink);
}

/* 开始备份清单。 */
static inline void
bbsink_begin_manifest(bbsink *sink)
{
	Assert(sink != NULL);

	sink->bbs_ops->begin_manifest(sink);
}

/* 处理清单中的一些内容。 */
static inline void
bbsink_manifest_contents(bbsink *sink, size_t len)
{
	Assert(sink != NULL);

	/* 查看 bbsink_archive_contents 中的注释。 */
	Assert(len > 0 && len <= sink->bbs_buffer_length);

	sink->bbs_ops->manifest_contents(sink, len);
}

/* 完成备份清单。 */
static inline void
bbsink_end_manifest(bbsink *sink)
{
	Assert(sink != NULL);

	sink->bbs_ops->end_manifest(sink);
}

/* 完成备份。 */
static inline void
bbsink_end_backup(bbsink *sink, XLogRecPtr endptr, TimeLineID endtli)
{
	Assert(sink != NULL);
	Assert(sink->bbs_state->tablespace_num == list_length(sink->bbs_state->tablespaces));

	sink->bbs_ops->end_backup(sink, endptr, endtli);
}

/* 在销毁之前释放资源。 */
static inline void
bbsink_cleanup(bbsink *sink)
{
	Assert(sink != NULL);

	sink->bbs_ops->cleanup(sink);
}

/* 转发回调。使用这些将操作传递给下一个接收器。 */
extern void bbsink_forward_begin_backup(bbsink *sink);
extern void bbsink_forward_begin_archive(bbsink *sink,
										 const char *archive_name);
extern void bbsink_forward_archive_contents(bbsink *sink, size_t len);
extern void bbsink_forward_end_archive(bbsink *sink);
extern void bbsink_forward_begin_manifest(bbsink *sink);
extern void bbsink_forward_manifest_contents(bbsink *sink, size_t len);
extern void bbsink_forward_end_manifest(bbsink *sink);
extern void bbsink_forward_end_backup(bbsink *sink, XLogRecPtr endptr,
									  TimeLineID endtli);
extern void bbsink_forward_cleanup(bbsink *sink);

/* 各种类型接收器的构造函数。 */
extern bbsink *bbsink_copystream_new(bool send_to_client);
extern bbsink *bbsink_gzip_new(bbsink *next, pg_compress_specification *);
extern bbsink *bbsink_lz4_new(bbsink *next, pg_compress_specification *);
extern bbsink *bbsink_zstd_new(bbsink *next, pg_compress_specification *);
extern bbsink *bbsink_progress_new(bbsink *next, bool estimate_backup_size);
extern bbsink *bbsink_server_new(bbsink *next, char *pathname);
extern bbsink *bbsink_throttle_new(bbsink *next, uint32 maxrate);

/* 额外的接口函数用于进度报告。 */
extern void basebackup_progress_wait_checkpoint(void);
extern void basebackup_progress_estimate_backup_size(void);
extern void basebackup_progress_wait_wal_archive(bbsink_state *);
extern void basebackup_progress_transfer_wal(void);
extern void basebackup_progress_done(void);

#endif
